
package cipher.util;

import java.lang.ref.SoftReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;

public class Util {

	// -- Caches --

	// The number of temp buffers in our pool
	private static final int TEMP_BUF_POOL_SIZE = 3;

	// Per-thread soft cache of the last temporary direct buffer
	private static ThreadLocal[] bufferPool;

	static {
		bufferPool = new ThreadLocal[TEMP_BUF_POOL_SIZE];
		for (int i = 0; i < TEMP_BUF_POOL_SIZE; i++)
			bufferPool[i] = new ThreadLocal();
	}

	public static ByteBuffer getTemporaryDirectBuffer(int size) {
		ByteBuffer buf = null;
		// Grab a buffer if available
		for (int i = 0; i < TEMP_BUF_POOL_SIZE; i++) {
			SoftReference ref = (SoftReference) (bufferPool[i].get());
			if ((ref != null) && ((buf = (ByteBuffer) ref.get()) != null)
					&& (buf.capacity() >= size)) {
				buf.rewind();
				buf.limit(size);
				bufferPool[i].set(null);
				return buf;
			}
		}

		// Make a new one
		return ByteBuffer.allocateDirect(size);
	}

	public static void releaseTemporaryDirectBuffer(ByteBuffer buf) {
		if (buf == null)
			return;
		// Put it in an empty slot if such exists
		for (int i = 0; i < TEMP_BUF_POOL_SIZE; i++) {
			SoftReference ref = (SoftReference) (bufferPool[i].get());
			if ((ref == null) || (ref.get() == null)) {
				bufferPool[i].set(new SoftReference(buf));
				return;
			}
		}
		// Otherwise replace a smaller one in the cache if such exists
		for (int i = 0; i < TEMP_BUF_POOL_SIZE; i++) {
			SoftReference ref = (SoftReference) (bufferPool[i].get());
			ByteBuffer inCacheBuf = (ByteBuffer) ref.get();
			if ((inCacheBuf == null)
					|| (buf.capacity() > inCacheBuf.capacity())) {
				bufferPool[i].set(new SoftReference(buf));
				return;
			}
		}
	}

	

	// Per-thread cached selector
	private static ThreadLocal localSelector = new ThreadLocal();
	// Hold a reference to the selWrapper object to prevent it from
	// being cleaned when the temporary selector wrapped is on lease.
	private static ThreadLocal localSelectorWrapper = new ThreadLocal();

	// When finished, invoker must ensure that selector is empty
	// -- Random stuff --

	static ByteBuffer[] subsequence(ByteBuffer[] bs, int offset, int length) {
		if ((offset == 0) && (length == bs.length))
			return bs;
		int n = length;
		ByteBuffer[] bs2 = new ByteBuffer[n];
		for (int i = 0; i < n; i++)
			bs2[i] = bs[offset + i];
		return bs2;
	}

	static <E> Set<E> ungrowableSet(final Set<E> s) {
		return new Set<E>() {

			public int size() {
				return s.size();
			}

			public boolean isEmpty() {
				return s.isEmpty();
			}

			public boolean contains(Object o) {
				return s.contains(o);
			}

			public Object[] toArray() {
				return s.toArray();
			}

			public <T> T[] toArray(T[] a) {
				return s.toArray(a);
			}

			public String toString() {
				return s.toString();
			}

			public Iterator<E> iterator() {
				return s.iterator();
			}

			public boolean equals(Object o) {
				return s.equals(o);
			}

			public int hashCode() {
				return s.hashCode();
			}

			public void clear() {
				s.clear();
			}

			public boolean remove(Object o) {
				return s.remove(o);
			}

			public boolean containsAll(Collection<?> coll) {
				return s.containsAll(coll);
			}

			public boolean removeAll(Collection<?> coll) {
				return s.removeAll(coll);
			}

			public boolean retainAll(Collection<?> coll) {
				return s.retainAll(coll);
			}

			public boolean add(E o) {
				throw new UnsupportedOperationException();
			}

			public boolean addAll(Collection<? extends E> coll) {
				throw new UnsupportedOperationException();
			}

		};
	}

	
	private static volatile Constructor directByteBufferConstructor = null;

	private static void initDBBConstructor() {
		AccessController.doPrivileged(new PrivilegedAction() {
			public Object run() {
				try {
					Class cl = Class.forName("java.nio.DirectByteBuffer");
					Constructor ctor = cl.getDeclaredConstructor(new Class[] {
							int.class, long.class, Runnable.class });
					ctor.setAccessible(true);
					directByteBufferConstructor = ctor;
				} catch (ClassNotFoundException x) {
					throw new InternalError();
				} catch (NoSuchMethodException x) {
					throw new InternalError();
				} catch (IllegalArgumentException x) {
					throw new InternalError();
				} catch (ClassCastException x) {
					throw new InternalError();
				}
				return null;
			}
		});
	}

	static MappedByteBuffer newMappedByteBuffer(int size, long addr,
			Runnable unmapper) {
		MappedByteBuffer dbb;
		if (directByteBufferConstructor == null)
			initDBBConstructor();
		try {
			dbb = (MappedByteBuffer) directByteBufferConstructor
					.newInstance(new Object[] { new Integer(size),
							new Long(addr), unmapper });
		} catch (InstantiationException e) {
			throw new InternalError();
		} catch (IllegalAccessException e) {
			throw new InternalError();
		} catch (InvocationTargetException e) {
			throw new InternalError();
		}
		return dbb;
	}

	private static volatile Constructor directByteBufferRConstructor = null;

	private static void initDBBRConstructor() {
		AccessController.doPrivileged(new PrivilegedAction() {
			public Object run() {
				try {
					Class cl = Class.forName("java.nio.DirectByteBufferR");
					Constructor ctor = cl.getDeclaredConstructor(new Class[] {
							int.class, long.class, Runnable.class });
					ctor.setAccessible(true);
					directByteBufferRConstructor = ctor;
				} catch (ClassNotFoundException x) {
					throw new InternalError();
				} catch (NoSuchMethodException x) {
					throw new InternalError();
				} catch (IllegalArgumentException x) {
					throw new InternalError();
				} catch (ClassCastException x) {
					throw new InternalError();
				}
				return null;
			}
		});
	}

	static MappedByteBuffer newMappedByteBufferR(int size, long addr,
			Runnable unmapper) {
		MappedByteBuffer dbb;
		if (directByteBufferRConstructor == null)
			initDBBRConstructor();
		try {
			dbb = (MappedByteBuffer) directByteBufferRConstructor
					.newInstance(new Object[] { new Integer(size),
							new Long(addr), unmapper });
		} catch (InstantiationException e) {
			throw new InternalError();
		} catch (IllegalAccessException e) {
			throw new InternalError();
		} catch (InvocationTargetException e) {
			throw new InternalError();
		}
		return dbb;
	}

	

}
